using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Admin.NET.Application.Entity;
using Admin.NET.Application.Entity.Business;
using Admin.NET.Application.EventBus;
using Admin.NET.Application.Service.Electricity;
using Admin.NET.Application.Service.Electricity.Dto;
using Admin.NET.Application.Service.Water.Dto;
using Admin.NET.Application.Util;
using Admin.NET.Core;
using Furion;
using Furion.DataEncryption;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.EventBus;
using Furion.FriendlyException;
using Mapster;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using SqlSugar;

namespace Admin.NET.Application.Service.Water;

[ApiDescriptionSettings(new string[] { "业务应用" }, Order = 100)]
public class WaterDeviceService : IDynamicApiController, ITransient, IPrivateDependency
{
	private readonly SqlSugarRepository<WaterDevice> _rep;

	private readonly SqlSugarRepository<WaterPriceRule> _rule;

	private readonly IEventPublisher _eventPublisher;

	private readonly SqlSugarRepository<RoomBalanceRecord> _balanceRep;

	private readonly RoomService _roomService;

	public WaterDeviceService(SqlSugarRepository<WaterDevice> rep, IEventPublisher eventPublisher, SqlSugarRepository<WaterPriceRule> rule, SqlSugarRepository<RoomBalanceRecord> balanceRep, RoomService roomService)
	{
		_rep = rep;
		_rule = rule;
		_eventPublisher = eventPublisher;
		_balanceRep = balanceRep;
		_roomService = roomService;
	}

	/// <summary>
	/// 分页查询水表
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpGet("/waterDevice/page")]
	public async Task<dynamic> Page([FromQuery] WaterDeviceInputDto input)
	{
		List<long> waterId = new List<long>();
		if (input.isBUZUYIDUN.GetValueOrDefault())
		{
			var deviceInfo = _rep.Context.Queryable<WaterDevice>().LeftJoin((WaterDevice o, WaterPriceRule cus) => o.PriceRuleId == (long?)cus.Id).LeftJoin((WaterDevice o, WaterPriceRule cus, Room roo) => o.RoomId == (long?)roo.Id)
				.Select((WaterDevice o, WaterPriceRule cus, Room roo) => new { o.Id, cus.Price, roo.Balance })
				.ToList();
			waterId = (from u in deviceInfo.Where(s =>
				{
					int result2;
					if (s.Price.HasValue)
					{
						decimal? price = s.Price;
						decimal balance = s.Balance;
						result2 = (((price.GetValueOrDefault() > balance) & price.HasValue) ? 1 : 0);
					}
					else
					{
						result2 = 0;
					}
					return (byte)result2 != 0;
				})
				select u.Id).ToList();
		}
		ISugarQueryable<WaterDeviceOut> query = from u in _rep.Context.Queryable<WaterDevice>().WhereIF(!string.IsNullOrWhiteSpace(input.Name), (WaterDevice u) => u.WaterName.Contains(input.Name.Trim())).WhereIF(!string.IsNullOrWhiteSpace(input.Sn), (WaterDevice u) => u.Sn.Contains(input.Sn.Trim()))
				.WhereIF(input.State.HasValue, (WaterDevice u) => (int?)u.WaterState == input.State)
				.WhereIF(input.RoomId.HasValue, (WaterDevice u) => u.RoomId == input.RoomId)
				.WhereIF(input.isBUZUYIDUN.GetValueOrDefault(), (WaterDevice u) => waterId.Contains(u.Id))
				.WhereIF(input.BuildingId.HasValue, (WaterDevice u) => u.BuildingId == input.BuildingId)
				.WhereIF(input.isPublicMeter.HasValue, (WaterDevice u) => u.isPublicMeter == input.isPublicMeter)
				.WhereIF(input.IsOnline.HasValue, (WaterDevice u) => (int?)u.IsOnline == input.IsOnline)
			select new WaterDeviceOut
			{
				Addr = u.Addr,
				LowAddr = u.LowAddr,
				Sn = u.Sn,
				BuildingId = u.BuildingId,
				CollectorId = u.CollectorId,
				BrandId = u.BrandId,
				WaterState = u.WaterState,
				CreateTime = u.CreateTime,
				Id = u.Id,
				IsDelete = u.IsDelete,
				IsOnline = u.IsOnline,
				LastOnlineTime = u.LastOnlineTime,
				PositiveDosage = u.PositiveDosage,
				PriceRuleId = u.PriceRuleId,
				ReverseUsage = u.ReverseUsage,
				RoomId = u.RoomId,
				TotalUsage = u.TotalUsage,
				LatestMeterReadingTime = u.LatestMeterReadingTime,
				RegionName = from s in SqlFunc.Subqueryable<Building>()
					where (long?)s.Id == u.BuildingId
					select s.Name,
				RuleName = from s in SqlFunc.Subqueryable<WaterPriceRule>()
					where (long?)s.Id == u.PriceRuleId
					select s.Name,
				TenantId = u.TenantId,
				RoomName = from s in SqlFunc.Subqueryable<Room>()
					where (long?)s.Id == u.RoomId
					select s.Name,
				Balance = from s in SqlFunc.Subqueryable<Room>()
					where (long?)s.Id == u.RoomId
					select s.Balance,
				WaterName = u.WaterName,
				UpdateTime = u.UpdateTime,
				ProtocolType = u.ProtocolType,
				UpdateUserId = u.UpdateUserId,
				UpdateUserName = u.UpdateUserName,
				Multiplier = u.Multiplier,
				AllTotalUsage = u.AllTotalUsage,
				LableId = u.LableId,
				isPublicMeter = u.isPublicMeter
			};
		query = query.OrderBuilder(input);
		SqlSugarPagedList<WaterDeviceOut> result = await query.ToPagedListAsync(input.Page, input.PageSize);
		IEnumerable<long?> roomId = result.Items.Select((WaterDeviceOut u) => u.RoomId).ToList().Distinct();
		var roomInfo = (from u in _rep.Context.Queryable<Room>()
			where roomId.Contains(u.Id)
			select new { u.Id, u.OwnerId }).ToList();
		IEnumerable<long?> ownerId = roomInfo.Select(u => u.OwnerId).ToList().Distinct();
		var ownerInfo = (from u in _rep.Context.Queryable<Owner>()
			where ownerId.Contains(u.Id)
			select new { u.Id, u.Name, u.PhoneNumber }).ToList();
		query.ForEach(delegate(WaterDeviceOut u)
		{
			var anon = ownerInfo.Where(s => s.Id == roomInfo.Where(q => q.Id == u.RoomId).FirstOrDefault()?.OwnerId).FirstOrDefault();
			u.UserId = anon?.Id;
			u.UserPhone = anon?.PhoneNumber;
			u.UserName = anon?.Name;
		});
		return result;
	}

	/// <summary>
	/// 增加水表
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpPost("/waterDevice/addapp")]
	public async Task Addapp(WaterDeviceAddDto input)
	{
		WaterDevice entity = input.Adapt<WaterDevice>();
		if (!input.BuildingId.HasValue && !string.IsNullOrEmpty(input.BuildingName))
		{
			Building building = new Building
			{
				Name = input.BuildingName
			};
			WaterDeviceAddDto waterDeviceAddDto = input;
			waterDeviceAddDto.BuildingId = await _rep.Context.Insertable(building).ExecuteReturnSnowflakeIdAsync();
		}
		if (!input.RoomId.HasValue && !string.IsNullOrEmpty(input.RoomName))
		{
			Room room = new Room
			{
				Name = input.RoomName,
				BuildingId = (input.BuildingId ?? long.MinValue)
			};
			if (!input.OwnerId.HasValue && !string.IsNullOrEmpty(input.Phone))
			{
				if (await (from u in _rep.Context.Queryable<Owner>().ClearFilter()
					where u.PhoneNumber == input.Phone
					select u).FirstAsync() != null)
				{
					await _rep.Context.Ado.RollbackTranAsync();
					throw Oops.Bah("手机号已被使用，请更换手机号");
				}
				Owner owner = new Owner
				{
					Name = input.UserName,
					PhoneNumber = input.Phone,
					Password = MD5Encryption.Encrypt(input.Phone),
					IsEnable = 1
				};
				Room room2 = room;
				room2.OwnerId = await _rep.Context.Insertable(owner).ExecuteReturnSnowflakeIdAsync();
			}
			if (input.OwnerId.HasValue)
			{
				room.OwnerId = input.OwnerId;
			}
			WaterDeviceAddDto waterDeviceAddDto2 = input;
			waterDeviceAddDto2.RoomId = await _rep.Context.Insertable(room).ExecuteReturnSnowflakeIdAsync();
		}
		else if (input.RoomId.HasValue)
		{
			Room roomedit = await (from u in _rep.Context.Queryable<Room>()
				where (long?)u.Id == input.RoomId
				select u).FirstAsync();
			roomedit.BuildingId = input.BuildingId ?? long.MinValue;
			if (!input.OwnerId.HasValue && !string.IsNullOrEmpty(input.Phone))
			{
				if (await (from u in _rep.Context.Queryable<Owner>().ClearFilter()
					where u.PhoneNumber == input.Phone
					select u).FirstAsync() != null)
				{
					await _rep.Context.Ado.RollbackTranAsync();
					throw Oops.Bah("手机号已被使用，请更换手机号");
				}
				Owner owner = new Owner
				{
					Name = input.UserName,
					PhoneNumber = input.Phone,
					Password = MD5Encryption.Encrypt(input.Phone),
					IsEnable = 1
				};
				Room room3 = roomedit;
				room3.OwnerId = await _rep.Context.Insertable(owner).ExecuteReturnSnowflakeIdAsync();
			}
			if (input.OwnerId.HasValue)
			{
				roomedit.OwnerId = input.OwnerId;
			}
			await _rep.Context.Updateable(roomedit).UpdateColumns((Room u) => new { u.BuildingId, u.OwnerId }).ExecuteCommandAsync();
		}
		entity.TotalUsage = input.AllTotalUsage;
		await _rep.InsertAsync(entity);
	}

	/// <summary>
	/// 增加水表
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpPost("/waterDevice/add")]
	public async Task Add(WaterDeviceAddDto input)
	{
		WaterDevice entity = input.Adapt<WaterDevice>();
		entity.TotalUsage = input.AllTotalUsage;
		await _rep.InsertAsync(entity);
	}

	/// <summary>
	/// 删除水表
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpPost("/waterDevice/delete")]
	public async Task Delete(DeletePriceRuleInput input)
	{
		WaterDevice entity = await _rep.GetFirstAsync((WaterDevice u) => u.Id == input.Id);
		await _rep.DeleteAsync(entity);
	}

	/// <summary>
	/// 更新水表
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpPost("/waterDevice/edit")]
	public async Task Update(WaterDevice input)
	{
		WaterDevice entity = input.Adapt<WaterDevice>();
		await _rep.Context.Updateable(entity).UpdateColumns((WaterDevice u) => new
		{
			u.WaterName, u.LableId, u.RoomId, u.BrandId, u.BuildingId, u.CollectorId, u.PriceRuleId, u.Multiplier, u.Addr, u.LowAddr,
			u.isPublicMeter
		}).ExecuteCommandAsync();
	}

	/// <summary>
	/// 更新水表
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpPost("/waterDevice/editapp")]
	public async Task Updateapp(WaterAppEditDto input)
	{
		WaterDevice entity = input.Adapt<WaterDevice>();
		Room room = await (from u in _rep.Context.Queryable<Room>()
			where (long?)u.Id == input.RoomId
			select u).FirstAsync();
		if (!input.UserId.HasValue && !string.IsNullOrEmpty(input.UserPhone))
		{
			if (await (from u in _rep.Context.Queryable<Owner>().ClearFilter()
				where u.PhoneNumber == input.UserPhone
				select u).FirstAsync() != null)
			{
				await _rep.Context.Ado.RollbackTranAsync();
				throw Oops.Bah("手机号已被使用，请更换手机号");
			}
			Owner owner = new Owner
			{
				Name = input.UserName,
				PhoneNumber = input.UserPhone,
				Password = MD5Encryption.Encrypt(input.UserPhone),
				IsEnable = 1
			};
			Room room2 = room;
			room2.OwnerId = await _rep.Context.Insertable(owner).ExecuteReturnSnowflakeIdAsync();
		}
		else if (input.UserId.HasValue)
		{
			room.OwnerId = input.UserId;
		}
		await _rep.Context.Updateable(room).UpdateColumns((Room u) => new { u.OwnerId }).ExecuteCommandAsync();
		await _rep.Context.Updateable(entity).UpdateColumns((WaterDevice u) => new
		{
			u.WaterName, u.LableId, u.RoomId, u.BrandId, u.BuildingId, u.CollectorId, u.PriceRuleId, u.Multiplier, u.Addr, u.LowAddr,
			u.isPublicMeter
		}).ExecuteCommandAsync();
	}

	/// <summary>
	/// 抄表
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpPost("/water/reading")]
	public async Task ReadingMeterWater(BaseIdInput input)
	{
		WaterDevice device = await (from u in _rep.AsQueryable()
			where u.Id == input.Id
			select u).FirstAsync();
		int newtype = 0;
		if (device != null)
		{
			string imei;
			if (device.ProtocolType == 0)
			{
				imei = device.Sn;
			}
			else
			{
				newtype = 1;
				Admin.NET.Application.Collector collector = await (from u in _rep.Context.Queryable<Admin.NET.Application.Collector>()
					where (long?)u.Id == device.CollectorId
					select u).FirstAsync();
				if (collector == null)
				{
					throw Oops.Bah("未有效关联采集设备");
				}
				if (collector.Status == 0)
				{
					throw Oops.Oh("采集器不在线");
				}
				imei = collector.Imei;
			}
			(IAggrementPlugin, BrandModel) plugin = AggrementPluginUtil.GetPlugin(device);
			IAggrementPlugin pluginService = plugin.Item1;
			string body = pluginService.ReadCombinedTotalE(code: plugin.Item2.Code, sn: imei, addr: device.LowAddr);
			if (!string.IsNullOrEmpty(body))
			{
				string value = body;
				string state = ((device.WaterState == 1) ? "0000" : "1111");
				Dictionary<string, WaterInfoMqttDto> dic = new Dictionary<string, WaterInfoMqttDto> { 
				{
					device.LowAddr,
					new WaterInfoMqttDto
					{
						State = state,
						Value = value
					}
				} };
				WaterService ePowerService = new WaterService();
				await ePowerService.WaterUpload(imei, newtype, dic);
				return;
			}
			throw Oops.Bah("抄表失败，请检查表地址和通讯线");
		}
		throw Oops.Oh(ErrorCodeEnum.xg1002);
	}

	/// <summary>
	/// 电表拉合闸
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpPost("/water/switch")]
	public async Task SwitchMeterWater(InputSwitch input, long? TenantId)
	{
		WaterDevice device = await (from u in _rep.AsQueryable()
			where u.Id == input.Id
			select u).FirstAsync();
		if (device != null)
		{
			string imei;
			if (device.ProtocolType == 0)
			{
				imei = device.Sn;
			}
			else
			{
				Admin.NET.Application.Collector collector = await (from u in _rep.Context.Queryable<Admin.NET.Application.Collector>()
					where (long?)u.Id == device.CollectorId
					select u).FirstAsync();
				if (collector == null)
				{
					throw Oops.Oh(ErrorCodeEnum.xg1002);
				}
				if (collector.Status == 0)
				{
					throw Oops.Oh("网关已离线");
				}
				imei = collector.Imei;
			}
			if (!TenantId.HasValue)
			{
				TenantId = long.Parse(App.User.FindFirst("TenantId").Value);
			}
			var (pluginService, currentModel) = AggrementPluginUtil.GetPlugin(device);
			if (currentModel.HasGate == 0 || string.IsNullOrWhiteSpace(currentModel.PowerOffCMD) || string.IsNullOrWhiteSpace(currentModel.PowerOnCMD))
			{
				throw Oops.Oh("当前水表不支持拉合闸");
			}
			if (input.GateStatus == 1)
			{
				pluginService.PowerOff(imei, device.LowAddr, currentModel.Code, currentModel.PowerOffCMD);
			}
			else
			{
				pluginService.PowerOn(imei, device.LowAddr, currentModel.Code, currentModel.PowerOnCMD);
			}
			if (device.ProtocolType == 0)
			{
				return;
			}
			int code = ((device.WaterState == 0) ? 1 : 0);
			await (from u in _rep.Context.Updateable<ElectricMeter>().SetColumns((ElectricMeter u) => u.GateStatus == code).SetColumns((ElectricMeter u) => u.SignalStatus == 1)
				where u.Id == device.Id
				select u).ExecuteCommandAsync();
			long orgId = 0L;
			long.TryParse(App.User?.FindFirst("OrgId")?.Value, out orgId);
			long userId = 0L;
			long.TryParse(App.User?.FindFirst("UserId")?.Value, out userId);
			string value = JsonConvert.SerializeObject(input);
			OperationLog operationLog = new OperationLog
			{
				OrgId = orgId,
				OrgName = App.User?.FindFirst("OrgName")?.Value,
				RealName = App.User?.FindFirst("RealName")?.Value,
				UserId = userId,
				IsDelete = false,
				Data = value,
				TenantId = TenantId,
				OperaId = device.Id,
				OperatName = "拉合闸",
				Operat = "name"
			};
			await _rep.Context.Insertable(operationLog).ExecuteCommandAsync();
			if (!device.RoomId.HasValue)
			{
				return;
			}
			Room room = await (from w in _rep.Context.Queryable<Room>()
				where w.TenantId == TenantId && (long?)w.Id == device.RoomId
				select w).FirstAsync();
			if (room == null || !room.OwnerId.HasValue)
			{
				return;
			}
			Owner owner = await (from w in _rep.Context.Queryable<Owner>()
				where w.TenantId == TenantId && (long?)w.Id == room.OwnerId
				select new Owner
				{
					Email = w.Email,
					Id = w.Id,
					AppId = w.AppId,
					OpenId = w.OpenId,
					PhoneNumber = w.PhoneNumber,
					Name = w.Name
				}).FirstAsync();
			if (owner != null)
			{
				string gatename = ((input.GateStatus == 1) ? "断闸" : "合闸");
				BillNoticePayload billNoticePayload = new BillNoticePayload
				{
					Body = $"您的水表：{device.WaterName}({device.Sn})已{gatename}。",
					Subject = "水表" + gatename,
					GateStatus = input.GateStatus,
					Owners = new List<Owner> { owner },
					MeterName = device.WaterName,
					RoomId = room.Id,
					RoomName = room.Name,
					Path = "/userPage/meterDetail/meterDetail?meterId=" + device.Id
				};
				EventPayload payload = new EventPayload
				{
					Data = JsonConvert.SerializeObject(billNoticePayload),
					TenantId = TenantId.Value
				};
				for (int i = 0; i < 4; i++)
				{
					_eventPublisher.PublishAsync($"{i}:SendGateNotice", payload).Wait();
				}
			}
			return;
		}
		throw Oops.Oh(ErrorCodeEnum.xg1002);
	}

	/// <summary>
	///
	/// </summary>
	/// <returns></returns>
	[AllowAnonymous]
	[NonUnify]
	[HttpPost("/water/upload")]
	public async Task UploadWaterInfo(WaterResponse water)
	{
		WaterDevice device = await (from u in _rep.AsQueryable()
			where u.Sn == water.data[0].meterNo
			select u).FirstAsync();
		if (device != null)
		{
			new WaterService();
			Dictionary<string, WaterInfoMqttDto> dic = new Dictionary<string, WaterInfoMqttDto> { 
			{
				device.LowAddr,
				new WaterInfoMqttDto
				{
					State = ((water.data[0].statusDoor == "阀开") ? "1" : "0"),
					Value = water.data[0].meterJbs
				}
			} };
			WaterService ePowerService = new WaterService();
			await ePowerService.WaterUpload(device.Sn, 0, dic);
		}
	}
}
